﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace iNet.Media.Imaging
{
	/// <summary>
	/// 提供色彩轉換相關功能。
	/// </summary>
	public static unsafe class ColorConversion
	{
		#region 靜態欄位
		static readonly byte** _YuvToB8Table;
		static readonly int** _YuvToG8Table;
		static readonly byte** _YuvToR8Table;
		static readonly double* _WeightedBForLuminance16;
		static readonly double* _WeightedGForLuminance16;
		static readonly double* _WeightedRForLuminance16;
		#endregion
		#region 型別建構子
		static ColorConversion()
		{
			//計算 YUV -> RGB888 轉換表
			_YuvToR8Table = (byte**)Marshal.AllocHGlobal(sizeof(byte*) * 256);
			_YuvToG8Table = (int**)Marshal.AllocHGlobal(sizeof(int*) * 256);
			_YuvToB8Table = (byte**)Marshal.AllocHGlobal(sizeof(byte*) * 256);
			for (int i = 255; i >= 0; --i)
			{
				_YuvToR8Table[i] = (byte*)Marshal.AllocHGlobal(sizeof(byte) * 256);
				_YuvToG8Table[i] = (int*)Marshal.AllocHGlobal(sizeof(int) * 256);
				_YuvToB8Table[i] = (byte*)Marshal.AllocHGlobal(sizeof(byte) * 256);
			}
			for (int y = 255; y >= 0; --y)
			{
				for (int v = 127; v >= -128; --v)
				{
					int r = (int)(y + 1.13983 * v);
					if (r < 0)
						r = 0;
					else if (r > 255)
						r = 255;
					_YuvToR8Table[y][v + 128] = (byte)r;
				}
				for (int u = 127; u >= -128; --u)
				{
					int b = (int)(y + 2.03211 * u);
					if (b < 0)
						b = 0;
					else if (b > 255)
						b = 255;
					_YuvToB8Table[y][u + 128] = (byte)b;
				}
			}
			for (int u = 127; u >= -128; --u)
			{
				for (int v = 127; v >= -128; --v)
				{
					int g = (int)(-0.39456 * u - 0.58060 * v);
					_YuvToG8Table[u + 128][v + 128] = g;
				}
			}

			//計算 16 位元轉換表
			_WeightedRForLuminance16 = (double*)Marshal.AllocHGlobal(65536 * sizeof(double));
			_WeightedGForLuminance16 = (double*)Marshal.AllocHGlobal(65536 * sizeof(double));
			_WeightedBForLuminance16 = (double*)Marshal.AllocHGlobal(65536 * sizeof(double));
			for (int i = 65535; i >= 0; --i)
			{
				_WeightedRForLuminance16[i] = (0.2126 * i);
				_WeightedGForLuminance16[i] = (0.7152 * i);
				_WeightedBForLuminance16[i] = (0.0722 * i);
			}
		}
		#endregion
		#region ToLuminance
		/// <summary>
		/// 將指定的 RGB 轉換成對應的亮度資訊。
		/// </summary>
		/// <param name="r">紅色資訊，範圍為 0 ~ 65535。</param>
		/// <param name="g">綠色資訊，範圍為 0 ~ 65535。</param>
		/// <param name="b">藍色資訊，範圍為 0 ~ 65535。</param>
		/// <returns>對應的亮度資訊，範圍為 0 ~ 65535。</returns>
		public static int ToLuminance(int r, int g, int b)
		{
			if (r < 0 || r > 65535)
				throw new ArgumentOutOfRangeException("r");
			if (g < 0 || g > 65535)
				throw new ArgumentOutOfRangeException("g");
			if (b < 0 || b > 65535)
				throw new ArgumentOutOfRangeException("b");
			return (int)(_WeightedRForLuminance16[r] + _WeightedGForLuminance16[g] + _WeightedBForLuminance16[b]);
		}
		#endregion
		#region ToLuminanceUnsafe
		/// <summary>
		/// 將指定的 RGB 轉換成對應的亮度資訊，但不會對 RGB 進行任何邊界確認。
		/// </summary>
		/// <param name="r">紅色資訊，範圍為 0 ~ 65535。</param>
		/// <param name="g">綠色資訊，範圍為 0 ~ 65535。</param>
		/// <param name="b">藍色資訊，範圍為 0 ~ 65535。</param>
		/// <returns>對應的亮度資訊，範圍為 0 ~ 65535。</returns>
		/// <remarks>若傳入的 RGB 超出範圍可能導致記憶體存取違規。</remarks>
		public static int ToLuminanceUnsafe(int r, int g, int b)
		{
			return (int)(_WeightedRForLuminance16[r] + _WeightedGForLuminance16[g] + _WeightedBForLuminance16[b]);
		}
		#endregion
		#region YuvToRgb888Unsafe
		/// <summary>
		/// 將指定的 YUV 色彩轉換成對應的 24 位元 RGB 色彩，但不會對 YUV 進行任何邊界確認。
		/// </summary>
		/// <param name="y">Y，範圍為 0 ~ 255。</param>
		/// <param name="u">U，範圍為 0 ~ 255。</param>
		/// <param name="v">V，範圍為 0 ~ 255。</param>
		/// <param name="r">對應的紅色資訊，範圍為 0 ~ 255。</param>
		/// <param name="g">對應的綠色資訊，範圍為 0 ~ 255。</param>
		/// <param name="b">對應的藍色資訊，範圍為 0 ~ 255。</param>
		public static void YuvToRgb888Unsafe(int y, int u, int v, out int r, out int g, out int b)
		{
			r = _YuvToR8Table[y][v];
			g = (y + _YuvToG8Table[u][v]);
			b = _YuvToB8Table[y][u];
			if (g < 0)
				g = 0;
			else if (g > 255)
				g = 255;
		}
		/// <summary>
		/// 將指定的 YUV 色彩轉換成對應的 24 位元 RGB 色彩，但不會對 YUV 及指標進行任何邊界確認。
		/// </summary>
		/// <param name="y">Y，範圍為 0 ~ 255。</param>
		/// <param name="u">U，範圍為 0 ~ 255。</param>
		/// <param name="v">V，範圍為 0 ~ 255。</param>
		/// <param name="r">儲存對應紅色資訊的位元組指標。</param>
		/// <param name="g">儲存對應綠色資訊的位元組指標。</param>
		/// <param name="b">儲存對應藍色資訊的位元組指標。</param>
		public static void YuvToRgb888Unsafe(int y, int u, int v, byte* r, byte* g, byte* b)
		{
			*r = _YuvToR8Table[y][v];
			*b = _YuvToB8Table[y][u];
			int intermediateG = (y + _YuvToG8Table[u][v]);
			if (intermediateG < 0)
				intermediateG = 0;
			else if (intermediateG > 255)
				intermediateG = 255;
			*g = (byte)intermediateG;
		}
		#endregion
	}
}
